home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / CShell⁄THINK C / AppleEvents.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-20  |  15.8 KB  |  587 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:        CShell
  5. ** File:        AppleEvents.c
  6. ** Written by:  Keith Rollin
  7. **
  8. ** Copyright © 1990-1991 Apple Computer, Inc.
  9. ** All rights reserved.
  10. **
  11. ** This code is completely based on the great work done by Keith Rollin.
  12. ** All I did was to add more comments than _anybody_ would want, make some
  13. ** things a little more general, and to set the code up so the the custom
  14. ** events are handled in a separate file.
  15. */
  16.  
  17.  
  18.  
  19. /*****************************************************************************/
  20.  
  21.  
  22.  
  23. #include "CShell.h"                /* Get the CShell includes/typedefs, etc.    */
  24. #include "CShellCommon.h"        /* Get the stuff in common with rez.        */
  25. #include "CShell.protos"        /* Get the prototypes for CShell.            */
  26.  
  27. #ifndef __GESTALTEQU__
  28. #include <GestaltEqu.h>
  29. #endif
  30.  
  31. #ifndef __UTILITIES__
  32. #include "Utilities.h"
  33. #endif
  34.  
  35.  
  36.  
  37. /*****************************************************************************/
  38.  
  39.  
  40.  
  41. #ifndef THINK_C
  42. #define rErrorAlert 129
  43. #endif
  44. #define kTimeOutInTicks (60 * 30)    /* 30 second timeout. */
  45.  
  46.  
  47.  
  48. /*****************************************************************************/
  49.  
  50.  
  51.  
  52. struct triplets{
  53.     AEEventClass    theEventClass;
  54.     AEEventID        theEventID;
  55.     ProcPtr            theHandler;
  56. };
  57. typedef struct triplets triplets;
  58. static triplets keywordsToInstall[] = {
  59.     { kCoreEventClass,        kAEOpenApplication,        (ProcPtr) DoAEOpenApplication },
  60.     { kCoreEventClass,        kAEOpenDocuments,        (ProcPtr) DoAEOpenDocuments },
  61.     { kCoreEventClass,        kAEPrintDocuments,        (ProcPtr) DoAEPrintDocuments },
  62.     { kCoreEventClass,        kAEQuitApplication,        (ProcPtr) DoAEQuitApplication }
  63.         /* The above are the four required AppleEvents. */
  64. };
  65.  
  66. Boolean        gHasAppleEvents = false;
  67. Boolean        gHasPPCToolbox  = false;
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72.  
  73.  
  74.  
  75. extern Boolean    gQuitApplication;
  76. extern Cursor    *gCurrentCursor;
  77. extern short    gPrintPage;
  78.  
  79.  
  80.  
  81. /*****************************************************************************/
  82. /*****************************************************************************/
  83.  
  84.  
  85.  
  86. /* DoHighLevelEvent
  87. **
  88. ** Simply calls AEProcessAppleEvent and reports any errors.
  89. ** AEProcessAppleEvent looks in its table of registered events and sees if
  90. ** the current event is registered.  If so, it calls the routine associated
  91. ** with that event.  In our case, we set that to DispatchAppleEvent.
  92. ** DispatchAppleEvent handles it in all cases.  DispatchAppleEvent uses the
  93. ** refCon to determine which AppleEvent it is doing, since it gets them all.
  94. */
  95.  
  96. #pragma segment AppleEvents
  97. void    DoHighLevelEvent(EventRecord *event)
  98. {
  99.     AEProcessAppleEvent(event);
  100. }
  101.  
  102.  
  103.  
  104. /*****************************************************************************/
  105.  
  106.  
  107.  
  108. /* GetTargetInfo
  109. **
  110. */
  111.  
  112. #pragma segment AppleEvents
  113. OSErr    GetTargetInfo(AEAddressDesc targetDesc, StringPtr zone,
  114.                       StringPtr machine, StringPtr application)
  115. {
  116.     ProcessSerialNumber targetPSN;
  117.     PortInfoRec            portInfo;
  118.     TargetID            theTargetID;
  119.     OSErr                err;
  120.     
  121.     zone[0]        = 0;
  122.     machine[0]     = 0;
  123.     application[0] = 0;
  124.     err = noErr;
  125.     
  126.     if (targetDesc.descriptorType == typeProcessSerialNumber) {
  127.         targetPSN = **(ProcessSerialNumber **)(targetDesc.dataHandle);
  128.         err = GetPortNameFromProcessSerialNumber(&portInfo.name, &targetPSN);
  129.         if (!err) pstrcpy((char *) application, (char *) &portInfo.name.name);
  130.         return(err);
  131.     }
  132.  
  133.     if (targetDesc.descriptorType == typeTargetID) {
  134.         theTargetID = **(TargetID **)(targetDesc.dataHandle);
  135.         switch (theTargetID.location.locationKindSelector) {
  136.             case ppcNoLocation:
  137.                 break;
  138.             case ppcNBPLocation:
  139.                 pstrcpy((char *) &zone,    (char *) &theTargetID.location.u.nbpEntity.zoneStr);
  140.                 pstrcpy((char *) &machine, (char *) &theTargetID.location.u.nbpEntity.objStr);
  141.                 break;
  142.             case ppcNBPTypeLocation:
  143.                 break;
  144.         }
  145.         pstrcpy((char *) &application, (char *) &theTargetID.name.name);
  146.         return(noErr);
  147.     }
  148.  
  149.     return(errAEWrongDataType);
  150. }
  151.  
  152.  
  153.  
  154. /*****************************************************************************/
  155.  
  156.  
  157.  
  158. /* InitAppleEvents
  159. **
  160. ** Intialize our AppleEvent dispatcher table.  For every triplet of entries in
  161. ** keywordsToInstall, we make a call to AEInstallEventHandler().
  162. */
  163.  
  164. #pragma segment AppleEvents
  165. void    InitAppleEvents(void)
  166. {
  167.     OSErr    err;
  168.     long    result;
  169.     short    i;
  170.  
  171.     gHasPPCToolbox  = (Gestalt(gestaltPPCToolboxAttr, &result) ? false : result != 0);
  172.     gHasAppleEvents = (Gestalt(gestaltAppleEventsAttr, &result) ? false : result != 0);
  173.  
  174.     if (gHasAppleEvents) {
  175.         for (i = 0; i < (sizeof(keywordsToInstall) / sizeof(triplets)); ++i) {
  176.             err = AEInstallEventHandler(
  177.                 keywordsToInstall[i].theEventClass,    /* What class to install.  */
  178.                 keywordsToInstall[i].theEventID,    /* Keywords to install.    */
  179.                 keywordsToInstall[i].theHandler,    /* The AppleEvent handler. */
  180.                 0L,                                    /* Unused refcon.           */
  181.                 false                                /* Only for our app.       */
  182.             );
  183.  
  184.             if (err) {
  185.                 Alert(rErrorAlert, nil);
  186.                 return;
  187.             }
  188.         }
  189.     }
  190. }
  191.  
  192.  
  193.  
  194. /*****************************************************************************/
  195.  
  196.  
  197.  
  198. /* MakeTarget
  199. **
  200. ** Creates a TargetID.
  201. **
  202. ** If sendDirect is TRUE, the target is specified by setting a
  203. ** ProcessSerialNumber to kCurrentProcess.  This has the advantage of sending
  204. ** the message directly to ourselves, bypassing ePPC and gaining about a 10-15x
  205. ** speed improvement.  If sendDirect is FALSE, we see if we have the
  206. ** PPCToolBox.  If not, then we are forced to do a direct send.  If we do have
  207. ** the PPCToolbox, then we call PPCBrowser.  We then look at the reply, and
  208. ** factor in the mode we are going to use in AESend.  If that mode is
  209. ** kAEWaitReply and the user selected us as the target, we have to turn that
  210. ** into a direct send.  This is because the AppleEvent Manager will otherwise
  211. ** post the event as a high-level event.  However, we are busy waiting for a
  212. ** reply, not looking for events, so we'll hang.  We avoid this by forcing a
  213. ** direct send.
  214. */
  215.  
  216. #pragma segment AppleEvents
  217. OSErr    MakeTarget(AEAddressDesc *target, Boolean sendDirect, short replyMode,
  218.                    Str255 prompt, Str255 applListLabel,PPCFilterProcPtr portFilter)
  219. {
  220.     OSErr                    err;
  221.     ProcessSerialNumber     targetPSN;
  222.     ProcessSerialNumber     myPSN;
  223.     TargetID                theTargetID;
  224.     Boolean                    sendingToSelf;
  225.  
  226.     static LocationNameRec    location;
  227.     static PortInfoRec        portInfo;
  228.     static Boolean            defaultOK = false;
  229.  
  230.     err = noErr;    /* Make sure we do the code for the second main if. */
  231.  
  232.     target->dataHandle = nil;
  233.         /* Assume we will fail and nil this descriptor out. */
  234.  
  235.     if (!sendDirect) {
  236.         if (!gHasPPCToolbox)
  237.             sendDirect = true;    /* No tools to send with, so send direct. */
  238.  
  239.         else {        /* We are not sending to self. */
  240.                     /* sendDirect is false.           */
  241.             err = PPCBrowser(
  242.                 prompt,            /* Browse dialog box prompt.           */
  243.                 applListLabel,    /* The 'programs' list title.           */
  244.                 defaultOK,        /* Initially false.                       */
  245.                 &location,        /* Correct if defaultOK is true.       */
  246.                 &portInfo,        /* Correct if defaultOK is true.       */
  247.                 portFilter,        /* No port filtering.                   */
  248.                 nil                /* List ports of type 'PPCToolBox'.       */
  249.             );
  250.  
  251.             if (!err) {                    /* If user didn't cancel... */
  252.                 defaultOK = true;        /* Default to the same port next time. */
  253.                 if (replyMode == kAEWaitReply) {
  254.                     /* Sender wants a reply and will be waiting... */
  255.  
  256.                     sendingToSelf = false;
  257.                         /* Assume that we aren't sending to ourselves. */
  258.  
  259.                     if (!location.locationKindSelector) {
  260.                         /* Hey, we are sending to ourselves! */
  261.  
  262.                         err = GetProcessSerialNumberFromPortName(
  263.                                 &portInfo.name, &targetPSN);
  264.                         if (!err) {
  265.                             GetCurrentProcess(&myPSN);
  266.                             err = SameProcess(&targetPSN, &myPSN, &sendingToSelf);
  267.                         }
  268.                     }
  269.  
  270.                     if (sendingToSelf) sendDirect = true;
  271.  
  272.                 }
  273.             }
  274.         }
  275.     }
  276.  
  277.     if (!err) {
  278.         if (sendDirect) {
  279.             /* Finally, we get to the point... */
  280.  
  281.             targetPSN.highLongOfPSN = 0;
  282.             targetPSN.lowLongOfPSN = kCurrentProcess;
  283.                 /* Process serial # is equal to kCurrentProcess.  This
  284.                 ** bypasses ePPC and speeds up things considerably. */
  285.  
  286.             err = AECreateDesc(
  287.                 typeProcessSerialNumber,    /* Standard PSN descriptor type. */
  288.                 (Ptr)&targetPSN,            /* "No ePPC" process serial #.     */
  289.                 sizeof(targetPSN),            /* Size of data (2 longs).         */
  290.                 target                        /* Wherefore art thou desc.         */
  291.             );
  292.         }
  293.         else {
  294.             theTargetID.location = location;
  295.             theTargetID.name     = portInfo.name;
  296.                 /* The fields sessionID does not need to be filled in now.
  297.                 ** The sessionID is returned when you actually connect to
  298.                 ** a port.  You can then use the sessionID from that point
  299.                 ** on to improve speed.
  300.                 **
  301.                 ** You also don't need to fill in the recvrName field at this
  302.                 ** point.  This is filled in, again, when the session is
  303.                 ** actually established.
  304.                 **
  305.                 ** The amount of data for a non-us target is bigger.
  306.                 ** We need the whole dealie for our target, since
  307.                 ** it is out on the net somewhere. */
  308.  
  309.             err = AECreateDesc(
  310.                 typeTargetID,            /* Standard target descriptor type. */
  311.                 (Ptr)&theTargetID,        /* The data for the descriptor.        */
  312.                 sizeof(theTargetID),    /* Size of the data.                */
  313.                 target                    /* Wherefore art thou desc.            */
  314.             );
  315.         }
  316.     }
  317.     return (err);
  318. }
  319.  
  320.  
  321.  
  322. /*****************************************************************************/
  323.  
  324.  
  325.  
  326. /* MissedAnyParameters
  327. **
  328. ** Used to check for any unread required parameters. Returns true if we
  329. ** missed at least one.
  330. */
  331.  
  332. #pragma segment AppleEvents
  333. Boolean    MissedAnyParameters(AppleEvent *message)
  334. {
  335.     OSErr        err;
  336.     DescType    ignoredActualType;
  337.     AEKeyword    missedKeyword;
  338.     Size        ignoredActualSize;
  339.     EventRecord    event;
  340.  
  341.     err = AEGetAttributePtr(    /* SEE IF PARAMETERS ARE ALL USED UP.          */
  342.         message,                /* AppleEvent to check.                          */
  343.         keyMissedKeywordAttr,    /* Look for unread parameters.                  */
  344.         typeKeyword,            /* So we can see what type we missed, if any. */
  345.         &ignoredActualType,        /* What is would have been if not coerced.      */
  346.         (Ptr)&missedKeyword,    /* Data area.  (Keyword not handled.)          */
  347.         sizeof(missedKeyword),    /* Size of data area.                          */
  348.         &ignoredActualSize        /* Actual data size.                          */
  349.     );
  350.  
  351. /* No error means that we found some unused parameters. */
  352.  
  353.     if (err == noErr) {
  354.         event.message = *(long *) &ignoredActualType;
  355.         event.where = *(Point *) &missedKeyword;
  356.         err = errAEEventNotHandled;
  357.     }
  358.  
  359. /* errAEDescNotFound means that there are no more parameters.  If we get
  360. ** an error code other than that, flag it.
  361. */
  362.  
  363.     return(err != errAEDescNotFound);
  364. }
  365.  
  366.  
  367.  
  368. /*****************************************************************************/
  369. /*****************************************************************************/
  370.  
  371.  
  372.  
  373. #pragma segment AppleEvents
  374. pascal OSErr    DoAEOpenApplication(AppleEvent *message, AppleEvent *reply, long refcon)
  375. {
  376. #pragma unused (message, refcon)
  377.  
  378.     FileRecHndl    frHndl;
  379.     OSErr        err;
  380.  
  381.     gCurrentCursor = nil;
  382.         /* Force re-calc of cursor region and cursor to use. */
  383.  
  384.     err = AppNewDocument(&frHndl);
  385.     if (!err)
  386.         if (err = AppNewWindow(frHndl, nil))
  387.             AppDisposeDocument(frHndl);
  388.  
  389.     AEPutParamPtr(                /* RETURN REPLY ERROR, EVEN IF NONE... */
  390.         reply,                    /* The AppleEvent.              */
  391.         keyReplyErr,            /* AEKeyword                 */
  392.         typeShortInteger,        /* Desired type.             */
  393.         (Ptr)&err,                /* Pointer to area for data. */ 
  394.         sizeof(short)            /* Size of data area.         */
  395.     );
  396.  
  397.     return(err);
  398. }
  399.  
  400.  
  401.  
  402. /*****************************************************************************/
  403.  
  404.  
  405.  
  406. #pragma segment AppleEvents
  407. pascal OSErr    DoAEOpenDocuments(AppleEvent *message, AppleEvent *reply, long refcon)
  408. {
  409. #pragma unused (refcon)
  410.  
  411.     OSErr        err;
  412.  
  413.     gCurrentCursor = nil;
  414.         /* Force re-calc of cursor region and cursor to use. */
  415.  
  416.     err = OpenDocEventHandler(message, reply, 0);
  417.  
  418.     AEPutParamPtr(                /* RETURN REPLY ERROR, EVEN IF NONE... */
  419.         reply,                    /* The AppleEvent.              */
  420.         keyReplyErr,            /* AEKeyword                 */
  421.         typeShortInteger,        /* Desired type.             */
  422.         (Ptr)&err,                /* Pointer to area for data. */ 
  423.         sizeof(short)            /* Size of data area.         */
  424.     );
  425.  
  426.     return(err);
  427. }
  428.  
  429.  
  430.  
  431. /*****************************************************************************/
  432.  
  433.  
  434.  
  435. #pragma segment AppleEvents
  436. pascal OSErr    DoAEPrintDocuments(AppleEvent *message, AppleEvent *reply, long refcon)
  437. {
  438. #pragma unused (refcon)
  439.  
  440.     OSErr        err;
  441.     short        openMode;
  442.  
  443.     gCurrentCursor = nil;
  444.         /* Force re-calc of cursor region and cursor to use. */
  445.  
  446.     openMode = 1;
  447.     if (!AEInteractWithUser(kTimeOutInTicks, nil, nil))
  448.         ++openMode;
  449.  
  450.     err = OpenDocEventHandler(message, reply, openMode);
  451.  
  452.     AEPutParamPtr(                /* RETURN REPLY ERROR, EVEN IF NONE... */
  453.         reply,                    /* The AppleEvent.              */
  454.         keyReplyErr,            /* AEKeyword                 */
  455.         typeShortInteger,        /* Desired type.             */
  456.         (Ptr)&err,                /* Pointer to area for data. */ 
  457.         sizeof(short)            /* Size of data area.         */
  458.     );
  459.  
  460.     return(err);
  461. }
  462.  
  463.  
  464.  
  465. /*****************************************************************************/
  466.  
  467.  
  468.  
  469. #pragma segment AppleEvents
  470. pascal OSErr    DoAEQuitApplication(AppleEvent *message, AppleEvent *reply, long refcon)
  471. {
  472. #pragma unused (message, refcon)
  473.  
  474.     OSErr    err;
  475.  
  476.     gCurrentCursor = nil;
  477.         /* Force re-calc of cursor region and cursor to use. */
  478.  
  479.     gQuitApplication = true;
  480.  
  481.     err = noErr;
  482.     AEPutParamPtr(                /* RETURN REPLY ERROR, EVEN IF NONE... */
  483.         reply,                    /* The AppleEvent.              */
  484.         keyReplyErr,            /* AEKeyword                 */
  485.         typeShortInteger,        /* Desired type.             */
  486.         (Ptr)&err,                /* Pointer to area for data. */ 
  487.         sizeof(short)            /* Size of data area.         */
  488.     );
  489.  
  490.     return(noErr);
  491. }
  492.  
  493.  
  494.  
  495. /*****************************************************************************/
  496.  
  497.  
  498.  
  499. /* OpenDocEventHandler
  500. **
  501. ** Called when we recieve an AppleEvent with an ID of "kAEOpenDocuments".
  502. ** This routine gets the direct parameter, parses it up into little FSSpecs,
  503. ** and opens each indicated file.  It also shows the technique to be used in
  504. ** determining if you are doing everything the AppleEvent record is telling
  505. ** you.  Parameters can be divided up into two groups: required and optional.
  506. ** Before executing an event, you must make sure that you've read all the
  507. ** required events.  This is done by making an "any more?" call to the
  508. ** AppleEvent manager.
  509. */
  510.  
  511. #pragma segment AppleEvents
  512. OSErr    OpenDocEventHandler(AppleEvent *message, AppleEvent *reply, short mode)
  513. {
  514. #pragma unused (reply)
  515.  
  516.     OSErr        err;
  517.     OSErr        err2;
  518.     AEDesc        theDesc;
  519.     FSSpec        theFSS;
  520.     short        loop;
  521.     long        numFilesToOpen;
  522.     AEKeyword    ignoredKeyWord;
  523.     DescType    ignoredType;
  524.     Size        ignoredSize;
  525.     FileRecHndl    frHndl;
  526.     WindowPtr    docWindow;
  527.  
  528.     theDesc.dataHandle = nil;
  529.         /* Make sure disposing of the descriptors is okay in all cases.
  530.         ** This will not be necessary after 7.0b3, since the calls that
  531.         ** attempt to create the descriptors will nil automatically
  532.         ** upon failure. */
  533.  
  534.     if (err = AEGetParamDesc(message, keyDirectObject, typeAEList, &theDesc))
  535.         return(err);
  536.  
  537.     if (!MissedAnyParameters(message)) {
  538.  
  539. /* Got all the parameters we need.  Now, go through the direct object,
  540. ** see what type it is, and parse it up. */
  541.  
  542.         err = AECountItems(&theDesc, &numFilesToOpen);
  543.         if (!err) {
  544.             /* We have numFilesToOpen that need opening, as either a window
  545.             ** or to be printed.  Go to it... */
  546.  
  547.             for (loop = 1; ((loop <= numFilesToOpen) && (!err)); ++loop) {
  548.                 err = AEGetNthPtr(        /* GET NEXT IN THE LIST...         */
  549.                     &theDesc,            /* List of file names.             */
  550.                     loop,                /* Item # in the list.             */
  551.                     typeFSS,            /* Item is of type FSSpec.         */
  552.                     &ignoredKeyWord,    /* Returned keyword -- we know.  */
  553.                     &ignoredType,        /* Returned type -- we know.     */
  554.                     (Ptr)&theFSS,        /* Where to put the FSSpec info. */
  555.                     sizeof(theFSS),        /* Size of the FSSpec info.         */
  556.                     &ignoredSize        /* Actual size -- we know.         */
  557.                 );
  558.                 if (err) break;
  559.  
  560.                 err = AppOpenDocument(&frHndl, &theFSS, fsRdWrPerm);
  561.                 if (err) break;
  562.  
  563.                 gPrintPage = mode;
  564.                     /* Open the window off-screen if we are printing. */
  565.                 if (err = AppNewWindow(frHndl, &docWindow))
  566.                     AppDisposeDocument(frHndl);
  567.                 else {
  568.                     if (gPrintPage) {
  569.                         err  = AppPrintDocument(frHndl, (mode == 2), (loop == 1));
  570.                         mode = 1;
  571.                         AppDisposeDocument(frHndl);
  572.                         DisposeAnyWindow(docWindow);
  573.                     }
  574.                 }
  575.                 gPrintPage = 0;
  576.             }
  577.         }
  578.     }
  579.     AppPrintDocument(nil, false, false);    /* Clean up after printing, if we did any. */
  580.  
  581.     err2 = AEDisposeDesc(&theDesc);
  582.     return(err ? err : err2);
  583. }
  584.  
  585.  
  586.  
  587.